/**
 **************************************************************************
 * @file     tcp_client.c
 * @version  v2.1.1
 * @date     2022-07-22
 * @brief    implement tcp client
 **************************************************************************
 *                       Copyright notice & Disclaimer
 *
 * The software Board Support Package (BSP) that is made available to
 * download from Artery official website is the copyrighted work of Artery.
 * Artery authorizes customers to use, copy, and distribute the BSP
 * software and its related documentation for the purpose of design and
 * development in conjunction with Artery microcontrollers. Use of the
 * software is governed by this copyright notice and the following disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED ON "AS IS" BASIS WITHOUT WARRANTIES,
 * GUARANTEES OR REPRESENTATIONS OF ANY KIND. ARTERY EXPRESSLY DISCLAIMS,
 * TO THE FULLEST EXTENT PERMITTED BY LAW, ALL EXPRESS, IMPLIED OR
 * STATUTORY OR OTHER WARRANTIES, GUARANTEES OR REPRESENTATIONS,
 * INCLUDING BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
 *
 **************************************************************************
 */

#include "tcp_client.h"
#include "lwip/tcp.h"
#include "common.h"
#include "mbcom.h"
#include "mbapp.h"
#include "bsp_api.h"
#include "os_obj.h"

mb_obj_type mb_client_obj;

extern struct tcp_pcb *tcp_active_pcbs;

/* Gloable functions ---------------------------------------------------------*/
unsigned char connect_flag = 0;
tcp_client_pcb_type tcp_client_obj;

/**
 * @brief  tcp client send data
 * @param  cpcb: the tcp_pcb that has sent the data
 * @param  buff: the packet buffer
 * @param  length: data size
 * @retval error value
 */
err_t tcp_client_send_data(struct tcp_pcb *cpcb, unsigned char *buff, unsigned int length)
{
	err_t err;

	err = tcp_write(cpcb, buff, length, TCP_WRITE_FLAG_COPY); // send data
	tcp_output(cpcb);
	return err;
}

/**
 * @brief  check connection
 * @param  none
 * @retval the tcp_pcb which accepted the connection
 */
struct tcp_pcb *check_tcp_connect(void)
{
	struct tcp_pcb *cpcb = 0;
	connect_flag = 0;
	for (cpcb = tcp_active_pcbs; cpcb != NULL; cpcb = cpcb->next)
	{
		if (cpcb->state == ESTABLISHED)
		{
			connect_flag = 1;
			break;
		}
	}

	if (connect_flag == 0)
	{
		tcp_client_init(TCP_CLIENT_LOCAL_PORT, TCP_CLIENT_SERVER_PORT, TCP_CLIENT_SERVER_IP);
		cpcb = 0;
	}
	return cpcb;
}

/**
 * @brief  report connection is establish
 * @param  arg: user supplied argument
 * @param  pcb: the tcp_pcb which accepted the connection
 * @param  err: error value
 * @retval err_t: it will response error code
 */
err_t tcp_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
	const char *port;
	if (pcb == tcp_client_obj.pcb01)
		port = "TCP1";
	if (pcb == tcp_client_obj.pcb02)
		port = "TCP2";
	if (pcb == tcp_client_obj.pcb03)
		port = "TCP3";
	if (pcb == tcp_client_obj.pcb04)
		port = "TCP4";
	if (pcb == tcp_client_obj.pcb05)
		port = "TCP5";

	dbg_print("%s connect %s , local port: %d , remote port: %d\n",
			  port, (err) ? ("ERR") : ("OK"), pcb->local_port, pcb->remote_port);

	return ERR_OK;
}

/**
 * @brief  receiving callback funtion for TCP client
 * @param  arg: the user argument
 * @param  pcb: the tcp_pcb that has received the data
 * @param  p: the packet buffer
 * @param  err: the error value linked with the received data
 * @retval error value
 */
err_t tcp_client_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
	if (p != NULL)
	{
		tcp_recved(pcb, p->tot_len);
		tcp_write(pcb, p->payload, p->tot_len, TCP_WRITE_FLAG_COPY);
		dbg_puts(p->payload);
		dbg_puts("\n");
		tcp_output(pcb);
	}
	else
	{
		tcp_close(pcb);
	}
	pbuf_free(p);
	err = ERR_OK;
	return err;
}

err_t tcp_mbclient_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
	if (p != NULL)
	{
		tcp_recved(pcb, p->tot_len);
		// tcp_write(pcb, p->payload, p->tot_len, TCP_WRITE_FLAG_COPY);
		// dbg_puts(p->payload);
		// dbg_puts("\n");

		memcpy(mb_client_obj.dat_buff, p->payload, p->tot_len);
		mb_tcp_rcv(&mb_client_obj, p->payload, p->tot_len);
		mb_client_obj.timeout_cnt = 0;
		tx_semaphore_put(os_obj.sid_eth);

		tcp_output(pcb);
	}
	else
	{
		tcp_close(pcb);
	}
	pbuf_free(p);
	err = ERR_OK;
	return err;
}

/**
 * @brief  initialize tcp client
 * @param  local_port: local port
 * @param  remote_port: remote port
 * @param  a: ip byte 0
 * @param  b: ip byte 1
 * @param  c: ip byte 2
 * @param  d: ip byte 3
 * @retval none
 */
void tcp_client_gen(uint16_t index,
					uint16_t local_port, uint16_t remote_port, ip_addr_t *ipaddr,
					err_t (*trecv)(void *, struct tcp_pcb *, struct pbuf *, err_t))
{
	struct tcp_pcb *tcp_client_pcb;
	err_t err;

	tcp_client_pcb = tcp_new();
	if (!tcp_client_pcb)
		return;

	(&(tcp_client_obj.pcb01))[index] = tcp_client_pcb;
	err = tcp_bind(tcp_client_pcb, IP_ADDR_ANY, local_port);
	if (err != ERR_OK)
		return;

	tcp_connect(tcp_client_pcb, ipaddr, remote_port, tcp_connected);
	tcp_recv(tcp_client_pcb, trecv);
}

void mb_tcp_client_init(void);
void tcp_client_init(uint16_t local_port, uint16_t remote_port, unsigned char a, unsigned char b, unsigned char c, unsigned char d)
{
	ip_addr_t ipaddr;
	IP4_ADDR(&ipaddr, a, b, c, d);

	tcp_client_gen(0, local_port + 0, remote_port + 0, &ipaddr, tcp_client_recv);
	tcp_client_gen(1, local_port + 1, remote_port + 0, &ipaddr, tcp_client_recv);
	tcp_client_gen(2, local_port + 2, remote_port + 0, &ipaddr, tcp_client_recv);
	tcp_client_gen(3, local_port + 3, 502, &ipaddr, tcp_mbclient_recv);
	tcp_client_gen(4, local_port + 4, 55502, &ipaddr, tcp_mbclient_recv);

	mb_tcp_client_init();
}

/**
 * @brief  tcp_adu_send
 * @param  none
 * @retval none
 */
static void tcp_adu_send(void)
{
	if (tcp_client_obj.pcb04->connected)
		tcp_write(tcp_client_obj.pcb04, mb_client_obj.dat_buff, mb_client_obj.snd_size, TCP_WRITE_FLAG_COPY);
	mb_send_end(&mb_client_obj);
}

/**
 * @brief  tcp_mb_timer
 * @param  none
 * @retval none
 */
void mb_tcp_client_timer(void)
{
	mb_rtu_timer_run(&mb_client_obj);
}

uint8_t mb_tcp_reconnect(void)
{
	return 1;
}

/**
 * @brief  initialize tcp server
 * @param  none
 * @retval none
 */
uu16 mb_tcp_dbuff[32];
void mbr_call_back(uint8_t cmd_site, uint8_t err_st);
void mbw_call_back(uint8_t cmd_site, uint8_t err_st);

void mb_tcp_client_init(void)
{
	mb_cmd_buff_type mcmd;
	static mb_cmd_buff_type cmdbuff[6];

	mb_obj_init(&mb_client_obj);
	mmb_cmd_buff_init(&mb_client_obj, cmdbuff, 6, 3);
	mb_client_obj.run_st.bits.rtu_mode = 0;
	mb_client_obj.run_st.bits.tcp_mode = 1;
	mb_client_obj.run_st.bits.master_mode = 1;
	mb_client_obj.slave.reg_input_cb = smb_reg_input_cb;
	mb_client_obj.slave.reg_hold_cb = smb_reg_holding_cb;
	mb_client_obj.slave.reg_coils_cb = smb_reg_coils_cb;
	mb_client_obj.slave.reg_discrete_cb = smb_reg_discrete_cb;
	mb_client_obj.adu_send = tcp_adu_send;
	mb_client_obj.id0_ack_en = 1;
	mb_client_obj.slave_id = 1;
	mb_client_obj.timeout_set = 500;
	mb_client_obj.rtu_master_delay_set = 0;
	mb_client_obj.eth_connect = mb_tcp_reconnect;

	// cmd config
	mcmd.device_id = 1;
	mcmd.cmd = FUN_CODE_WRITE_MULTIPLE_REG;
	mcmd.dat = mb_tcp_dbuff;
	mcmd.dat_addr = 1;
	mcmd.amount = 7;
	mcmd.call_back = mbw_call_back;
	mmb_stc_cmd_req(&mb_client_obj, 0, &mcmd);

	mcmd.cmd = FUN_CODE_READ_REG;
	mcmd.dat = &mb_tcp_dbuff[8];
	mcmd.dat_addr = 8;
	mcmd.call_back = mbr_call_back;
	mmb_stc_cmd_req(&mb_client_obj, 1, &mcmd);

	app_timer_task_add(mb_tcp_client_timer);
}

/**
 * @brief  tcp poll
 * @param  none
 * @retval none
 */
void tcp_mbclient_poll(void)
{
	uu32 tick = app_tick_get();

	if (tcp_client_obj.pcb04->connected)
		mb_poll(&mb_client_obj);

	loop(8)
		mb_tcp_dbuff[index] = tick >> (2 * index);
}

void mbr_call_back(uint8_t cmd_site, uint8_t err_st)
{
	if (err_st)
	{
		dbg_print("mb rd err :%d\n", err_st);
	}
}
void mbw_call_back(uint8_t cmd_site, uint8_t err_st)
{
	if (err_st)
	{
		dbg_print("mb wr err :%d\n", err_st);
	}
}